﻿using System;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;


namespace CoreEx.Wcf.Common
{
    /// <summary>
    /// Intercepts calls to a WCF service and creates and closes
    /// the <see cref="IChannel"/> between calls.
    /// </summary>
    [Implements(typeof(IInvokeWrapper), ServiceName = "ServiceContractInvokeWrapper")]
    public class ServiceContractInvokeWrapper : IInvokeWrapper
    {
        private object _actualTarget;
        private IChannelFactory _channelFactory;


        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceContractInvokeWrapper"/> class.
        /// </summary>
        /// <param name="channelFactory">The channel factory.</param>
        public ServiceContractInvokeWrapper(IChannelFactory channelFactory)
        {
            _channelFactory = channelFactory;            
        }



        /// <summary>
        /// This method will be called just before the actual
        /// method call is executed.
        /// </summary>
        /// <param name="info">The <see cref="T:LinFu.AOP.Interfaces.IInvocationInfo"/> associated with the method call.</param>
        /// <seealso cref="T:LinFu.AOP.Interfaces.IInvocationInfo"/>
        public virtual void BeforeInvoke(IInvocationInfo info)
        {
            _actualTarget = _channelFactory.GetType().InvokeMember("CreateChannel",
                                                                   BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null,
                                                                   _channelFactory, null);                                                                                                                                                                                                       
        }

        /// <summary>
        /// This method will be called immediately after the actual
        /// method call is executed.
        /// </summary>
        /// <param name="info">The <see cref="T:LinFu.AOP.Interfaces.IInvocationInfo"/> associated with the method call.</param>
        /// <param name="returnValue">The value returned from the actual method call.</param>
        public virtual void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            var channel = (IChannel) _actualTarget;
            try
            {
                channel.Close();
            }
            catch (Exception)
            {                
                channel.Abort();
            }
        }

        /// <summary>
        /// This method will provide the actual implementation
        /// for the <see cref="P:LinFu.AOP.Interfaces.IInvocationInfo.TargetMethod">target method</see>
        /// instance.
        /// </summary>
        /// <param name="info">The <see cref="T:LinFu.AOP.Interfaces.IInvocationInfo"/> associated with the method call.</param>
        /// <returns>
        /// The actual return value from the <see cref="P:LinFu.AOP.Interfaces.IInvocationInfo.TargetMethod"/>.
        /// </returns>
        public virtual object DoInvoke(IInvocationInfo info)
        {
            return _actualTarget.GetType().DynamicInvoke(_actualTarget, info.TargetMethod.Name, info.Arguments); 
        }
    }
}